﻿using System;
using System.Linq;
using GameOn.Web.Entities;
using GameOn.Web.Services.Helpers;

namespace GameOn.Web.Services
{
    /// <summary>
    ///   A service for quickly adding match and game results
    /// </summary>
    public class MatchResultEntryService : IMatchResultEntryService
    {
        private const string PlayerVPlayerNameFormat = "{0} v {1}";
        private IRatingHelper RatingHelper { get; set; }
        private IRankHistoryLoggingService RankHistoryLoggingService { get; set; }
        private IPlayerService PlayerService { get; set; }
        private ITimelineService TimelineService { get; set; }

        public MatchResultEntryService(GameOnEntities entities)
        {
            Entities = entities;
            RatingHelper = new DurrantEloRatingHelper();
            RankHistoryLoggingService = new RankHistoryLoggingService(entities);
            PlayerService = new PlayerService(entities);
            TimelineService = new TimelineService();
        }

        private GameOnEntities Entities { get; set; }

        /// <summary>
        ///   Adds a Match, returning the match ID
        /// </summary>
        /// <param name = "playedDate">The date the match was played</param>
        /// <param name = "player1Id">Player 1's ID</param>
        /// <param name = "player2Id">Player 2's ID</param>
        /// <param name="winnerId">The winning player's ID</param>
        /// <param name="score">The score of the match, expressed as a string. e.g. 10-3, 3-10, 10-12</param>
        /// <returns>The new Match ID</returns>
        private Match AddMatch(DateTime playedDate, int player1Id, int player2Id, int winnerId, string score)
        {
            //TODO: Reuse
            var match = new Match
                            {
                                PlayedDate = playedDate,
                                Player1 = Entities.Players.FirstOrDefault(p => p.Id == player1Id),
                                Player2 = Entities.Players.FirstOrDefault(p => p.Id == player2Id),
                                Score = score
                            };

            if (match.Player1 == null) throw new ArgumentException("Player 1 ID is not valid", "player1Id");
            if (match.Player2 == null) throw new ArgumentException("Player 2 ID is not valid", "player2Id");
            if (match.Player1 == match.Player2) throw new ArgumentException("Players 1 and 2 may not be the same");

            match.Name = string.Format(PlayerVPlayerNameFormat, match.Player1.Name, match.Player2.Name);

            if (winnerId == match.Player1.Id) match.Winner = match.Player1;
            else if (winnerId == match.Player2.Id) match.Winner = match.Player2;
            else throw new ArgumentException("winnerId is invalid");

            match.EnforceInvariants();
            Entities.CreateObjectSet<Match>().AddObject(match);
            Entities.SaveChanges();
            return match;
        }

        /// <summary>
        /// Adds a new Match, marked as played, and updates the rankings of each player based on who won, and their current rankings
        /// </summary>
        /// <param name="player1">Player 1's ID</param>
        /// <param name="player2">Player 2's ID</param>
        /// <param name="winnerId">The winner's player ID</param>
        /// <param name="score">Optional. The score expressed as a string</param>
        /// <returns>A Match entity expressing the results of the match</returns>
        public Match Played(int player1, int player2, int winnerId, string score)
        {
            var match = AddMatch(DateTime.Now, player1, player2, winnerId, score);

            // Get current leader
            var leader = PlayerService.GetTopRankingPlayer();

            // Calculate new rankings
            RatingHelper.UpdateMatchPlayersRanks(match);
            Entities.SaveChanges();

            SaveRankHistory(match.Player1);
            SaveRankHistory(match.Player2);

            // timeline the match
            TimelineService.AddMessage("{0} beat {1} {2}", match.Winner.Name, match.Loser.Name, match.Score);

            // check if leader has changed, if so add to timeline
            var newLeader = PlayerService.GetTopRankingPlayer();

            if (leader != newLeader)
            {
                TimelineService.AddMessage("{0} has replaced {1} as the new top ranking player with a rating of {2}!", match.Winner.Name, leader.Name, match.Winner.CurrentRank);
            }

            return match;
        }

        private void SaveRankHistory(Player player)
        {
            RankHistoryLoggingService.SaveRankHistory(player);
        }
    }
}